package ljl.bilibili.chat.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import ljl.bilibili.chat.entity.PPTDetail;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

import static ljl.bilibili.chat.constant.Constant.*;

/**
 * 大模型处理类，用于向大模型提问并获取大模型响应
 */
@Slf4j
public class PPTHandler extends WebSocketListener {
    //历史对话记录列表
    public List<RoleContent> historyList = new ArrayList<>();
    //存储大模型的总回答
    public String totalAnswer = "";
    //存储新问题
    public String NewQuestion = "";
    //Gson实例，用于JSON解析
    public static final Gson gson = new Gson();
    //PPT详情对象
    public PPTDetail pptDetail = new PPTDetail();
    //应用ID
    private String appId;
    //API密钥
    private String apiKey;
    //API密钥
    private String apiSecret;

    /**
     * 将大模型的响应作为事件发布
     */
//    public void publishMessage(String message) {
//        log.info("publish");
//           MessageEvent messageEvent = new MessageEvent(this, message);
//           applicationEventPublisher.publishEvent(messageEvent);
//    }

    /**
     * 构造函数，初始化PPT详情和API参数
     */
    public PPTHandler(String text, String appId, String apiKey, String apiSecret) {
        // 设置PPT的主题名称
        pptDetail.setThemeName(text);
        // 禁用生成结束标记
        pptDetail.setGenerateEnding(false);
        // 清空文本内容
        pptDetail.setText("");
        // 初始化类成员变量
        this.apiKey = apiKey;
        this.appId = appId;
        this.apiSecret = apiSecret;
    }

    /**
     * 向大模型提问
     */
    public void send(String text) throws Exception {
        System.out.print("我：");
        // 创建一个新的问题字符串，包含用户的输入，并限定回答的长度
        NewQuestion = text + ",限定在150字以内";
        // 生成获取认证令牌的URL
        String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
        // 初始化OkHttpClient对象，用于发起网络请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        // 将认证URL从HTTP转换为WebSocket协议
        String url = authUrl.replace("http://", "ws://").replace("https://", "wss://");
        // 构建请求对象
        Request request = new Request.Builder().url(url).build();
        // 初始化总的回复内容字符串
        totalAnswer = "";
        // 通过WebSocket建立连接
        WebSocket webSocket = client.newWebSocket(request, this);
        // 启动消息发送和接收的处理流程
        run(webSocket);
    }

    /**
     * 检查是否可以添加历史对话记录，如果记录过长则删除前面一部分
     */
    public boolean canAddHistory() {
        // 初始化历史记录总长度为0
        int history_length = 0;
        // 遍历历史记录列表，累加每个历史记录的长度
        for (RoleContent temp : historyList) {
            history_length = history_length + temp.content.length();
        }
        // 如果历史记录总长度超过12000个字符
        if (history_length > 12000) {
            // 移除最早添加的5条历史记录，以减少总长度
            historyList.remove(0);
            historyList.remove(1);
            historyList.remove(2);
            historyList.remove(3);
            historyList.remove(4);
            // 返回false，表示当前不可以添加新的历史记录
            return false;
        } else {
            // 如果历史记录总长度未超过阈值，返回true，表示可以添加新的历史记录
            return true;
        }
    }


    /**
     * 将与大模型的连接传递给发送消息方法
     */
    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        System.out.print("大模型：");
    }

    /**
     * 监听大模型响应并发布事件与记录对应用户会话内容
     */
    @Override
    public void onMessage(WebSocket webSocket, String text) {
        try {
            // 将接收到的文本消息解析为JsonParse对象
            JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
            // 检查消息的状态码
            Integer status = myJsonParse.header.status;
            // 如果状态码不为0，表示发生错误
            if (myJsonParse.header.code != 0) {
                // 打印错误信息和请求的sid
                System.out.println("发生错误，错误码为：" + myJsonParse.header.code);
                System.out.println("本次请求的sid为：" + myJsonParse.header.sid);
                // 关闭WebSocket连接
                webSocket.close(1000, "");
            }
            // 如果消息的有效载荷不为空
            if (myJsonParse.payload != null) {
                // 如果有效载荷中的choices不为空
                if (myJsonParse.payload.choices != null) {
                    // 如果choices中的text不为空且不为空列表
                    if (myJsonParse.payload.choices.text != null && myJsonParse.payload.choices.text.size() > 0) {
                        // 获取text列表并遍历每个Text对象
                        List<Text> textList = myJsonParse.payload.choices.text;
                        for (Text temp : textList) {
                            // 将Text对象的内容追加到pptDetail的文本中
                            pptDetail.setText(pptDetail.getText() + temp.content);
                        }
                    }
                }
            }
            // 如果消息的状态码为2，表示处理结束
            if (myJsonParse.header.status == 2) {
                // 设置生成结束标志
                pptDetail.setGenerateEnding(true);
                // 打印分隔线表示一个处理周期结束
                System.out.println();
                System.out.println("*************************************************************************************");
                // 如果可以添加历史记录
                if (canAddHistory()) {
                    // 创建新的RoleContent对象，设置角色为ASSISTANT_ROLE，内容为totalAnswer
                    RoleContent roleContent = new RoleContent();
                    roleContent.setRole(ASSISTANT_ROLE);
                    roleContent.setContent(totalAnswer);
                    // 将新的历史记录添加到historyList中
                    historyList.add(roleContent);
                } else {
                    // 如果不能添加历史记录，移除最早的一条记录
                    historyList.remove(0);
                    // 创建新的RoleContent对象，设置角色为ASSISTANT_ROLE，内容为totalAnswer
                    RoleContent roleContent = new RoleContent();
                    roleContent.setRole(ASSISTANT_ROLE);
                    roleContent.setContent(totalAnswer);
                    // 将新的历史记录添加到historyList中
                    historyList.add(roleContent);
                }
                // 重置totalAnswer为下一个周期做准备
                totalAnswer = "";
            }
        } catch (Exception e) {
            // 打印异常信息
            e.printStackTrace();
        }
    }

    /**
     * 当WebSocket连接失败时调用
     */
    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                System.out.println("onFailure code:" + code);
                System.out.println("onFailure body:" + response.body().string());
                if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    /**
     * 获取鉴权URL
     */
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        // 解析主机URL以获取主机名和路径
        URI uri = new URI(hostUrl);
        // 创建一个日期格式化器，用于生成请求日期字符串
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 构建用于生成签名的预设字符串
        String preStr = "host: " + uri.getHost() + "\n" +
                "date: " + date + "\n" +
                "GET " + uri.getPath() + " HTTP/1.1";
        // 定义加密类型为hmac-sha256
        String encryptType = "hmacsha256";
        // 创建一个Mac实例，用于生成签名
        Mac mac = Mac.getInstance(encryptType);
        // 创建一个密钥规范，使用API密钥秘密作为密钥
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), encryptType);
        mac.init(spec);
        // 生成签名并进行Base64编码
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // 构建认证字符串
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // 构建查询字符串，包括认证信息、日期和主机名
        String query = String.format("authorization=%s&date=%s&host=%s",
                URLEncoder.encode(Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8)), "UTF-8"),
                URLEncoder.encode(date, "UTF-8"),
                URLEncoder.encode(uri.getHost(), "UTF-8"));
        // 返回带有认证信息的URL
        return "wss://" + uri.getHost() + uri.getPath() + "?" + query;
    }

    class JsonParse {
        Header header;
        Payload payload;
    }

    class Header {
        int code;
        int status;
        String sid;

        public int getStatus() {
            return status;
        }
    }

    class Payload {
        Choices choices;
    }

    class Choices {
        List<Text> text;
    }

    class Text {
        String role;
        String content;
    }

    class RoleContent {
        String role;
        String content;

        public String getRole() {
            return role;
        }

        public void setRole(String role) {
            this.role = role;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }

    /**
     * 封装请求json发送给大模型
     */
    public void run(WebSocket webSocket) {
        try {
            // 创建请求的JSON对象
            JSONObject requestJson = new JSONObject();
            // 创建请求头部信息
            JSONObject header = new JSONObject();
            header.put(MODEL_APP_ID, appId);
            header.put(SESSION_UID, UUID.randomUUID().toString().substring(0, 10));
            // 创建请求参数
            JSONObject parameter = new JSONObject();
            JSONObject chat = new JSONObject();
            chat.put(MODEL_DOMAIN, MODEL_DOMAIN_VALUE);
            chat.put(MODEL_TEMPERATURE, 1.0);
            chat.put(MODEL_MAX_TOKENS, 8000);
            parameter.put(MODEL_PARAMETER, chat);
            // 创建请求负载
            JSONObject payload = new JSONObject();
            JSONObject message = new JSONObject();
            JSONArray text = new JSONArray();
            // 如果有历史消息，将其添加到请求中
            if (historyList.size() > 0) {
                for (RoleContent tempRoleContent : historyList) {
                    text.add(JSON.toJSON(tempRoleContent));
                }
            }
            // 创建新的用户消息并添加到请求中
            RoleContent roleContent = new RoleContent();
            roleContent.role = USER_ROLE;
            roleContent.content = NewQuestion;
            log.info(NewQuestion);
            text.add(JSON.toJSON(roleContent));
            historyList.add(roleContent);
            message.put(MESSAGE_TEXT, text);
            payload.put(PAYLOAD_MESSAGE, message);
            // 将头部信息、参数和负载添加到请求JSON中
            requestJson.put(REQUEST_HEADER, header);
            requestJson.put(REQUEST_PARAMTER, parameter);
            requestJson.put(REQUEST_PAYLOAD, payload);
            // 发送请求JSON到WebSocket
            webSocket.send(requestJson.toString());
            // 等待一段时间后关闭WebSocket连接
            while (true) {
                Thread.sleep(200);
                break;
            }
            webSocket.close(1000, "");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}